﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using log4net;
using System.Configuration;
using Impinj.OctaneSdk;
using LH.Comunicacion;
using LH.Utilitario;
using LH.Entidad;


namespace IRTPRFID1
{
    public partial class ServicioRFID : ServiceBase
    {

        #region --- Variables y Propiedades ---
        Timer timerServicioLectura = new Timer();
        private static readonly ILog log = LogManager.GetLogger(typeof(ServicioRFID));

        private Servidor servidor = null;

        private ImpinjReader reader = null;

        private Status status = null;

        private const int TIEMPO_ESPERA_COMANDO = 7500;
        private const string SEPARADOR_PARAMETRO = "|";
        private const string SIGNO_ESTACIONARIO = "Estacionario";

        private const string LLAVE_TIEMPO_ESPERA_LECTURA = "TiempoEsperaLectura";

        private RFIDState estadoReader;
        private enum RFIDState
        {
            CONECTADO,
            DESCONECTADO
        }
        // Cabecera de identificacion de etiqueta
        private string IDENTIFICADOR_CAB_ETIQUETA = "00";
        #endregion

        #region --- Constructor y Destructor ---
        public ServicioRFID()
        {
            timerServicioLectura.Elapsed += timerServicioLectura_Elapsed;
            log4net.Config.XmlConfigurator.Configure();

            InitializeComponent();
        }
        #endregion

        #region --- Eventos del Servicio ---
        protected override void OnStart(string[] args)
        {
            try
            {
                log.Debug("------------------------------");
                log.Debug("Iniciando Servicio Lectura ... v20140930.0303");

                // Estableciendo parametros del timer del servicio
                timerServicioLectura.Interval = 1000 * Convert.ToInt16(ConfigurationManager.AppSettings[Constantes.SEGUNDOS_ENTRE_CADA_VERIFICACION]);
                timerServicioLectura.Enabled = true;
                timerServicioLectura.Start();

                this.IDENTIFICADOR_CAB_ETIQUETA = ConfigurationManager.AppSettings[Constantes.CABECERA_EPC];

                // ------
                log.Debug("Iniciando Servidor de comunicaciones ...");
                bool resulServidor = iniciarServidor();
                if (resulServidor)
                {
                    log.Debug("Se inicio el servidor de comunicaciones");
                }

                // ------
                log.Debug("Iniciando conexion con Reader ...");
                this.estadoReader = RFIDState.DESCONECTADO;

                bool resultado = conectar();
                if (resultado)
                {
                    log.Debug("Se establecio conexion con Reader - " + ConfigurationManager.AppSettings[Constantes.IPREADER]);

                    resultado = iniciarLectura();
                    if (resultado)
                    {
                        log.Debug("Se inicio la lectura del Reader");
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }
        }

        protected override void OnStop()
        {
            try
            {
                timerServicioLectura.Enabled = false;

                log.Debug("-------------------------------");
                log.Debug("Deteniendo Servicio Lectura ... v20140930.0303");

                // -------
                log.Debug("Deteniendo conexion con el  Reader ... ");
                detenerLectura();

                bool resultado = desconectar();
                if (resultado)
                {
                    log.Debug("Se finalizo conexion con Reader - " + ConfigurationManager.AppSettings[Constantes.IPREADER]);
                }

                // -------
                log.Debug("Deteniendo Servidor de comunicaciones");
                bool resulServidor = detenerServidor();
                if (resulServidor)
                {
                    log.Debug("Se detuvo el servidor de comunicaciones");
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }
        }
        #endregion

        #region --- Evento del Timer ---
        private void timerServicioLectura_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                timerServicioLectura.Stop();
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }
            finally
            {

                timerServicioLectura.Start();
            }
        }
        #endregion

        #region --- Eventos del Reader ---

        private void OnTagsReported(ImpinjReader sender, TagReport report)
        {
            if (this.estadoReader == RFIDState.DESCONECTADO) { return; }

            try
            {

                // Validar si hay tag leidos
                if (report.Tags.Count == 0) { return; }

                lock (this)
                {
                    // Loop por cada etiqueta que lea del report.
                    foreach (Tag tag in report)
                    {

                        // Validar que sea una etiqueta con la cabecera de servir
                        if (!esEtiquetaValida(tag.Epc.ToString().Replace(" ", ""))) { return; }

                        // Validar si esta activo el comando de Lectura de Tag
                        if (ConfigurationManager.AppSettings[Constantes.ACTIVAR_COMANDO_LEC].ToString().Equals(Constantes.SI))
                        {
                            //cadena a enviar
                           
                            Etiqueta etiqueta = new Etiqueta();

                            etiqueta.antena = tag.AntennaPortNumber.ToString();
                            etiqueta.epc = tag.Epc.ToString().Replace(" ", "");
                            etiqueta.fecha = DateTime.Now;
                            etiqueta.pcBits = tag.PcBits;
                            etiqueta.peakRSSIInDbm = tag.PeakRssiInDbm;
                            etiqueta.phaseAngleInRadians = tag.PhaseAngleInRadians;
                            etiqueta.reader = ConfigurationManager.AppSettings[Constantes.IPREADER];
                            etiqueta.tagSeenCount = tag.TagSeenCount;
                            
                            enviarServidor(etiqueta.SerializarToXml());
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }
        }
        #endregion

        #region --- Eventos del ServidorComunciaciones ---
        private void servidor_sucesosServidor(string mensaje)
        {
            log.Error(mensaje);
        }

        private void servidor_recepcionData(string mensaje, string ipClientePuerto)
        {
            log.Debug(ipClientePuerto + " -> " + mensaje);
        }

        private void servidor_desconexionCliente(string mensaje, string ipClientePuerto)
        {
            log.Debug(mensaje);
        }

        private void servidor_conexionCliente(string mensaje, string ipClientePuerto)
        {
            log.Debug(mensaje);
        }
        #endregion

        #region --- Procedimientos OctaneSDK ---
        private void inicializarReader()
        {
            this.reader = new ImpinjReader();
            log.Debug("Reader Inicializado");
            // Capturado eventos de lectura de Tags
            this.reader.TagsReported += OnTagsReported;
        }

        private void finalizarReader()
        {
            this.reader.TagsReported -= OnTagsReported;
            this.reader = null;
        }

        private bool conectar()
        {
            bool resultado = false;
            try
            {
                inicializarReader();

                this.reader.Connect(ConfigurationManager.AppSettings[Constantes.IPREADER]);

                this.reader.ApplyDefaultSettings();

                Settings settings = reader.QueryDefaultSettings();

                int indiceReaderMode = 0;
                indiceReaderMode = Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.INDICE_READER_MODE]);

                // Estableciendo Reader Mode
                switch (indiceReaderMode)
                {
                    case 0://AutoSet Dense Reader
                        settings.ReaderMode = ReaderMode.AutoSetDenseReader;
                        break;
                    case 1://AutoSet Single Reader
                        settings.ReaderMode = ReaderMode.AutoSetSingleReader;
                        break;
                    case 2://Max Throughput
                        settings.ReaderMode = ReaderMode.MaxThroughput;
                        break;
                    case 3://Hybrid Mode
                        settings.ReaderMode = ReaderMode.Hybrid;
                        break;
                    case 4://Dense Reader (M=4)
                        settings.ReaderMode = ReaderMode.DenseReaderM4;
                        break;
                    case 5://Dense Reader (M=8)
                        settings.ReaderMode = ReaderMode.DenseReaderM8;
                        break;
                    case 6://MaxMiller
                        settings.ReaderMode = ReaderMode.MaxMiller;
                        break;
                }

                int indiceSearchMode = 0;
                indiceSearchMode = Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.INDICE_SEARCH_MODE]);

                // Estableciendo Search Mode
                switch (indiceSearchMode)
                {
                    case 0://Dual Target
                        settings.SearchMode = SearchMode.DualTarget;
                        break;
                    case 1://Single Target Inventory
                        settings.SearchMode = SearchMode.SingleTarget;
                        break;
                    case 2://Single Target w/Supression
                        settings.SearchMode = SearchMode.TagFocus;
                        break;
                    case 3://Desconocido
                        settings.SearchMode = SearchMode.ReaderSelected;
                        break;
                }

                // Estableciendo Sesion
                settings.Session = Convert.ToUInt16(ConfigurationManager.AppSettings[Constantes.INDICE_SESSION]);

                settings.Antennas.DisableAll();

                settings.Antennas.GetAntenna(1).IsEnabled = ConfigurationManager.AppSettings[Constantes.ACTIVAR_ANTENA1].ToString().Equals(Constantes.SI);
                settings.Antennas.GetAntenna(9).IsEnabled = ConfigurationManager.AppSettings[Constantes.ACTIVAR_ANTENA2].ToString().Equals(Constantes.SI);
                settings.Antennas.GetAntenna(17).IsEnabled = ConfigurationManager.AppSettings[Constantes.ACTIVAR_ANTENA3].ToString().Equals(Constantes.SI);
                settings.Antennas.GetAntenna(25).IsEnabled = ConfigurationManager.AppSettings[Constantes.ACTIVAR_ANTENA4].ToString().Equals(Constantes.SI);

                if (settings.Antennas.GetAntenna(1).IsEnabled)
                {
                    settings.Antennas.GetAntenna(1).MaxRxSensitivity = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA1]);
                    settings.Antennas.GetAntenna(1).MaxTransmitPower = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA1]);
                    settings.Antennas.GetAntenna(1).TxPowerInDbm = Convert.ToDouble(ConfigurationManager.AppSettings[Constantes.POTENCIA_ANTENA1]);
                    settings.Antennas.GetAntenna(1).RxSensitivityInDbm = Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.SENSIBILIDAD_ANTENA1]);
                }

                if (settings.Antennas.GetAntenna(9).IsEnabled)
                {
                    settings.Antennas.GetAntenna(9).MaxRxSensitivity = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA2]);
                    settings.Antennas.GetAntenna(9).MaxTransmitPower = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA2]);
                    settings.Antennas.GetAntenna(9).TxPowerInDbm = Convert.ToDouble(ConfigurationManager.AppSettings[Constantes.POTENCIA_ANTENA2]);
                    settings.Antennas.GetAntenna(9).RxSensitivityInDbm = Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.SENSIBILIDAD_ANTENA2]);
                }

                if (settings.Antennas.GetAntenna(17).IsEnabled)
                {
                    settings.Antennas.GetAntenna(17).MaxRxSensitivity = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA3]);
                    settings.Antennas.GetAntenna(17).MaxTransmitPower = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA3]);
                    settings.Antennas.GetAntenna(17).TxPowerInDbm = Convert.ToDouble(ConfigurationManager.AppSettings[Constantes.POTENCIA_ANTENA3]);
                    settings.Antennas.GetAntenna(17).RxSensitivityInDbm = Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.SENSIBILIDAD_ANTENA3]);
                }

                if (settings.Antennas.GetAntenna(25).IsEnabled)
                {
                    settings.Antennas.GetAntenna(25).MaxRxSensitivity = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA4]);
                    settings.Antennas.GetAntenna(25).MaxTransmitPower = (Constantes.SI == ConfigurationManager.AppSettings[Constantes.TODA_CAPACIDAD_ANTENA4]);
                    settings.Antennas.GetAntenna(25).TxPowerInDbm = Convert.ToDouble(ConfigurationManager.AppSettings[Constantes.POTENCIA_ANTENA4]);
                    settings.Antennas.GetAntenna(25).RxSensitivityInDbm = Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.SENSIBILIDAD_ANTENA4]);
                }

                settings.Report.IncludeAntennaPortNumber = true;
                settings.Report.IncludePeakRssi = true;
                settings.Report.IncludePhaseAngle = true;
                settings.Report.IncludeChannel = true;
                settings.Report.IncludeFirstSeenTime = true;
                settings.Report.IncludeLastSeenTime = true;
                settings.Report.IncludeSeenCount = true;
                settings.Report.IncludeFastId = true;
                settings.Report.Mode = ReportMode.Individual;

                // Estableciendo los nuevos parametros de configuracion
                this.reader.ApplySettings(settings);

                // Estableciendo identificador(MAC) del ReaderRFID
                //this.reader.ReaderIdentity = this.reader.QueryFeatureSet().ReaderIdentity;
                this.status = this.reader.QueryStatus();

                this.estadoReader = RFIDState.CONECTADO;

                resultado = true;
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }

            return resultado;
        }

        private bool desconectar()
        {
            bool resultado = false;

            try
            {
                if (this.estadoReader == RFIDState.DESCONECTADO) { return true; }

                resultado = detenerLectura();

                if (resultado == false)
                {
                    this.reader.Disconnect();
                }

                finalizarReader();

                this.estadoReader = RFIDState.DESCONECTADO;

                resultado = true;
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }

            return resultado;
        }

        private string obtenerInformacion()
        {
            StringBuilder cadenaInformacion = null;
            try
            {
                if (this.estadoReader == RFIDState.DESCONECTADO)
                {
                    log.Debug("Primero debe establecer conexion con el Reader");
                    return string.Empty;
                }

                cadenaInformacion = new StringBuilder();

                FeatureSet features = this.reader.QueryFeatureSet();

                cadenaInformacion.Append("Nombre del Modelo: " + features.ModelName);
                cadenaInformacion.Append(Environment.NewLine);
                cadenaInformacion.Append("Numero del Modelo: " + features.ModelNumber);
                cadenaInformacion.Append(Environment.NewLine);
                cadenaInformacion.Append("Numero de Serie: " + features.SerialNumber);
                cadenaInformacion.Append(Environment.NewLine);
                //cadenaInformacion.Append("Indetificación del Reader: " + this.status.ReaderIdentity.ToString());
                //cadenaInformacion.Append(Environment.NewLine);
                cadenaInformacion.Append("Version del Firmware: " + features.FirmwareVersion);
                cadenaInformacion.Append(Environment.NewLine);
                //cadenaInformacion.Append("Version del Octane software: " + features.SoftwareVersion);
                //cadenaInformacion.Append(Environment.NewLine);
                cadenaInformacion.Append("Cantidad de Antenas: " + features.AntennaCount);

                log.Debug(cadenaInformacion.ToString());
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }

            return cadenaInformacion.ToString();
        }

        private bool obtenerEstadoConexion()
        {
            bool conectado = false;
            try
            {
                Status status = this.reader.QueryStatus();
                conectado = status.IsConnected == true;
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }

            return conectado;
        }

        private bool iniciarLectura()
        {
            bool resultado = false;
            try
            {
                if (this.estadoReader == RFIDState.DESCONECTADO) { return resultado; }

                if (!this.reader.QueryStatus().IsSingulating)
                {
                    this.reader.Start();
                }

                resultado = true;
            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }

            return resultado;
        }

        private bool detenerLectura()
        {
            bool resultado = false;
            try
            {
                if (this.estadoReader == RFIDState.DESCONECTADO)
                {
                    log.Debug("Primero debe establecer conexion con el Reader");
                    return resultado;
                }

                if (this.reader.QueryStatus().IsSingulating)
                {
                    this.reader.Stop();
                }

                resultado = true;

            }
            catch (Exception ex)
            {
                log.Error(ex.Message, ex);
            }

            return resultado;
        }

        #endregion

        #region --- Procedimentos ServidorComunicaciones ---
        private bool iniciarServidor()
        {
            this.servidor = new Servidor(Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.SERVIDOR_MAXIMO_CLIENTES]));
            this.servidor.recepcionData += new Servidor.recepcionDataHandler(servidor_recepcionData);
            this.servidor.sucesosServidor += new Servidor.sucesosServidorHandler(servidor_sucesosServidor);
            this.servidor.conexionCliente += new Servidor.conexionClienteHandler(servidor_conexionCliente);
            this.servidor.desconexionCliente += new Servidor.desconexionClienteHanler(servidor_desconexionCliente);

            return this.servidor.iniciar(Convert.ToInt32(ConfigurationManager.AppSettings[Constantes.SERVIDOR_PUERTO]));
        }

        private bool detenerServidor()
        {
            bool resultado = false;

            resultado = this.servidor.detener();
            this.servidor.recepcionData -= new Servidor.recepcionDataHandler(servidor_recepcionData);
            this.servidor.sucesosServidor -= new Servidor.sucesosServidorHandler(servidor_sucesosServidor);
            this.servidor.conexionCliente -= new Servidor.conexionClienteHandler(servidor_conexionCliente);
            this.servidor.desconexionCliente -= new Servidor.desconexionClienteHanler(servidor_desconexionCliente);

            this.servidor = null;

            return resultado;
        }

        private delegate void mensajeAEnviar(string mensaje);

        private void enviarServidor(string mensaje)
        {
            //this.servidor.enviarMensaje(mensaje);
            mensajeAEnviar method = new mensajeAEnviar(envioMensajeAsincrono);
            IAsyncResult a = method.BeginInvoke(mensaje, null, null);
        }

        private void envioMensajeAsincrono(string mensaje)
        {
            this.servidor.enviarMensaje(mensaje);
        }

        #endregion

        #region --- Procedimientos Propios ---


        private bool validarTiempoLectura(DateTime tiempoLecturaAnterior)
        {
            TimeSpan tiempoDiferencia = DateTime.Now - tiempoLecturaAnterior;
            int tiempoEsperaLectura = 0;

            tiempoEsperaLectura = Convert.ToInt32(ConfigurationManager.AppSettings[LLAVE_TIEMPO_ESPERA_LECTURA]);

            return tiempoDiferencia.TotalSeconds >= tiempoEsperaLectura;
        }



        private bool esLectorRFIDValido(string identificador)
        {
            bool encontrado = false;
            string parametrosIdentificadorLector = string.Empty;
            string[] listaIndentificadorLector;

            parametrosIdentificadorLector = ConfigurationManager.AppSettings[Constantes.IDENTIFICADOR_READER].ToString();
            parametrosIdentificadorLector = parametrosIdentificadorLector.Desencriptar();

            listaIndentificadorLector = parametrosIdentificadorLector.Split(SEPARADOR_PARAMETRO.ToCharArray());

            foreach (string identificadorLector in listaIndentificadorLector)
            {
                if (identificadorLector.Equals(identificador))
                {
                    encontrado = true;
                    break;
                }
            }

            return encontrado;
        }

        public bool esEtiquetaValida(string epc)
        {
            epc = epc.Replace(" ", "");
            string cabecera = epc.Substring(0, 8);
            return cabecera.Equals(this.IDENTIFICADOR_CAB_ETIQUETA);
        }
        #endregion

    }
}
